/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.commons.util.geo;

/**
 * 根据地球表面两点的经纬度坐标，计算两点间的距离
 *
 * <p>https://www.cnblogs.com/zhoug2020/p/8993750.html</p>
 *
 * @author liym
 * @since 2020-01-14 11:04 新建
 */
public final class GeoUtil {

    /**
     * 地球半径平均值（单位：km）
     * <p>
     * https://github.com/antirez/redis/blob/unstable/src/geohash_helper.c
     */
    private static final double EARTH_MEAN_RADIUS_KM = 6372.797560856D;

    private GeoUtil() {}

    /**
     * 根据地球表面两点的经纬度坐标，计算两点间的距离
     *
     * @param lng1d 经度1
     * @param lat1d 纬度1
     * @param lng2d 经度2
     * @param lat2d 纬度2
     * @return 距离（单位：km）
     * @since 2020-01-14 11:05:05
     */
    public static double geoDistance(final double lng1d, final double lat1d, final double lng2d, final double lat2d) {
        // 将角度转换成弧度
        double lng1r = Math.toRadians(lng1d);
        double lat1r = Math.toRadians(lat1d);
        double lng2r = Math.toRadians(lng2d);
        double lat2r = Math.toRadians(lat2d);

        // 差值
        double vLng = Math.abs(lng1r - lng2r);
        double vLat = Math.abs(lat1r - lat2r);

        // 计算地球大圆（Great-circle，地球球心切面）距离，弧度表示
        double h = haversin(vLat) + Math.cos(lat1r) * Math.cos(lat2r) * haversin(vLng);

        return 2.0D * EARTH_MEAN_RADIUS_KM * Math.asin(Math.sqrt(h));
    }

    /**
     * Half-Versine 公式
     */
    private static double haversin(double theta) {
        double v = Math.sin(theta / 2.0D);
        return v * v;
    }

}
